/*
 *	FilePoller.cpp
 *
 *
 *
*/

#include "FilePoller.h"
#include "MD5.h"

FilePoller::~FilePoller() {
	this->diferencias.clear();
	this->newFiles.clear();
}

void FilePoller::poll() {
	/*	Entro al directorio
	 * 	Voy recorriendo los files del dir (opendir, readdir, closedir)
	 * 	Abro, recalculo hash, tomo fecha de modificacion y agrego al archivo auxiliar
	 * 	Mergeo archivo auxiliar nuevo con el viejo indice
	 * 	Obtengo indice actualizado mientras listo diferencias para enviar al server
	 * 	El cliente debe pedirle al Poller la lista de diferencias
	 * */
	std::list<std::string> listaNombreArchivos;

	// Con este stream trabajaremos con cada uno de los archivos del repo
	std::fstream archivoAux;
	int comparacion = 0;

	this->oldHashes.clear();
	this->newFiles.clear();
	this->deletedFiles.clear();
	this->diferencias.clear();

	std::cout << "Obtengo lista de archivos del repo..." << std::endl;
	this->getListaArchivosRepo(listaNombreArchivos);
	
	// declaro cada uno de los atributos de un archivo a utilizar
	std::string nombreArchivoIndice, hashArchivoIndice, hashArchivoRepo;
	FileDateModification fechaModificacionRepo, fechaModificacionIndice;

	// Si no tengo archivos en el repo, no tengo que pollear
	if (listaNombreArchivos.size() == 0) {
		// Obtengo todos los archivos borrados
		this->getArchivoIndice(this->indiceHashes, 
		nombreArchivoIndice, hashArchivoIndice, 
		fechaModificacionIndice);
		while (nombreArchivoIndice.compare("") != 0) {
			this->deletedFiles.push_back(nombreArchivoIndice);
			this->diferencias.push_back(new
					DiferenciaDeletedFile(nombreArchivoIndice.c_str()));
			this->getArchivoIndice(this->indiceHashes, 
			nombreArchivoIndice, hashArchivoIndice, 
			fechaModificacionIndice);
		}
		this->indiceHashes.close();
		this->indiceHashes.open(this->indexPath.c_str(), std::fstream::out);
		return;
	}

	// Abro indice auxiliar
	std::string indiceAuxPath = this->dirPath;
	indiceAuxPath += "indice/indiceAux";
	std::fstream indiceAux(indiceAuxPath.c_str(), std::fstream::out);
	if (indiceAux.fail()) {
		throw MyException("No se pudo abrir el indice auxiliar para pollear");
	}

	this->indiceHashes.close();
	this->indiceHashes.open(indexPath.c_str(), std::ios_base::in);

	std::cout << "Comienzo a mergear y actualizar repo" << std::endl;
	//	Recorro la lista de archivos, y voy actualizando el indice local
	std::list<std::string>::iterator it=listaNombreArchivos.begin();
	// Leo el primer archivo del indice local actual
	this->getArchivoIndice(indiceHashes, nombreArchivoIndice, hashArchivoIndice,
			fechaModificacionIndice);
	bool leerRepo = true;

	while ((it != listaNombreArchivos.end()) &&
			(nombreArchivoIndice.compare("") != 0)) {
		if (leerRepo)
			this->getArchivoRepo(archivoAux, hashArchivoRepo,
					fechaModificacionRepo, it);
		// 	Comparo los nombres del archivo siguiente en el indice con el del
		//	repo para mergear
		comparacion = (*it).compare(nombreArchivoIndice);

		if (comparacion == 0) {
			// Caso: nombres iguales (ver si hay diferencia en fecha de modificacion)
			if (fechaModificacionRepo.compare(fechaModificacionIndice) != 0) {
				//Comparo Hashes
				if (hashArchivoRepo.compare(hashArchivoIndice) != 0) {
					this->oldHashes.push_back(hashArchivoIndice);
					this->diferencias.push_back(new
							DiferenciaModifiedFile(it->c_str(),
									fechaModificacionRepo, this->dirPath,
									hashArchivoIndice));
				}
			}
			// Actualizo el indice auxiliar con el nuevo archivo
			escribirArchivoAIndice(indiceAux, *it, hashArchivoRepo,
					fechaModificacionRepo);

			std::cout << "Actualizando indice..." << std::endl;
			std::cout << "Obtengo nuevos archivos a comparar..." << std::endl;

			archivoAux.close();
			++it;
			this->getArchivoIndice(indiceHashes, nombreArchivoIndice,
					hashArchivoIndice, fechaModificacionIndice);
			leerRepo = true;
		} else if (comparacion < 0) {
			std::cout << "Archivo nuevo en el repo..." << std::endl;

			this->newFiles.push_back(*it);
			this->diferencias.push_back(new DiferenciaNewFile(it->c_str(),
					this->dirPath));
			escribirArchivoAIndice(indiceAux, *it, hashArchivoRepo,
					fechaModificacionRepo);
			archivoAux.close();
			++it;
			leerRepo = true;
		} else {
			std::cout << "Archivo borrado en el repo..." << std::endl;

			this->deletedFiles.push_back(nombreArchivoIndice);
			this->diferencias.push_back(new
					DiferenciaDeletedFile(nombreArchivoIndice.c_str()));
			this->getArchivoIndice(indiceHashes, nombreArchivoIndice,
					hashArchivoIndice, fechaModificacionIndice);
			leerRepo = false;
		}
	}

	// Si salgo del while, puede ser porque termino el indice o la lista de archivos en repo
	if (it == listaNombreArchivos.end()) {
		// Archivo borrados en el repo
		while (nombreArchivoIndice.compare("") != 0) {
			this->deletedFiles.push_back(nombreArchivoIndice);
			this->diferencias.push_back(new
					DiferenciaDeletedFile(nombreArchivoIndice.c_str()));
			this->getArchivoIndice(indiceHashes, nombreArchivoIndice,
					hashArchivoIndice, fechaModificacionIndice);
		}
	} else {
		// Archivos nuevos en el repo
		do {
			this->getArchivoRepo(archivoAux, hashArchivoRepo,
					fechaModificacionRepo, it);
			std::cout << "Archivo nuevo " << *it << std::endl;
			this->newFiles.push_back(*it);
			this->diferencias.push_back(new DiferenciaNewFile(it->c_str(),
					this->dirPath));
			escribirArchivoAIndice(indiceAux, *it, hashArchivoRepo,
					fechaModificacionRepo);
			++it;
			archivoAux.close();
		} while (it != listaNombreArchivos.end());
	}

	// Borro indice desactualizado y renombro el nuevo
	this->indiceHashes.close();
	remove(this->indexPath.c_str());
	rename(indiceAuxPath.c_str(), this->indexPath.c_str());
	indiceAux.close();
	this->indiceHashes.open(this->indexPath.c_str(), std::fstream::in);
}

void FilePoller::getListaArchivosRepo(std::list<std::string>& listaNombres) {
	DIR *dir;
	struct dirent *ent;
	/*	Recorro el directorio listando todos los archivos encontrados,
	 * calculando hash y sumandolos a un indice auxiliar
	*/
	dir = opendir(this->dirPath.c_str());
	if (!dir) {
		throw MyException("Poller) No se ha encontrado el directorio a pollear");
	}
	while ((ent = readdir(dir)) != NULL) {
		if (ent->d_type == DT_REG) {
			// Suma el nombre del archivo a la lista de archivos del directorio AU
			listaNombres.push_back(ent->d_name);
		}
	}
	closedir (dir);
	//  Me ordena la lista por nombre de archivo
	listaNombres.sort();
}

std::list<Diferencia*> FilePoller::getListaDiferencias() {
	return (this->diferencias);
}

void FilePoller::getArchivoIndice(std::fstream& indice, std::string& nombre,
		std::string& hash, FileDateModification& fechaMod) {
	nombre.clear();
	hash.clear();
	if (indice.good()) {
		indice >> nombre;
		indice >> hash;
		indice >> fechaMod;
	}
}

void FilePoller::getArchivoRepo(std::fstream& archivoAux, std::string& hashArchivoRepo,
		FileDateModification& fechaModificacionRepo,
		std::list<std::string>::iterator& it) {
	std::string fullPath = this->dirPath;
	fullPath += *it;

	std::string buff = obtenerContenidosArchivo(fullPath.c_str());

	hashArchivoRepo = md5(buff.c_str());
	fechaModificacionRepo.createFileModDate(fullPath.c_str());
}

//	Estructura del indice: nombreArchivo, hash, fechaModificacion, 1 archivo por linea
void FilePoller::escribirArchivoAIndice(std::fstream& indice,
		const std::string& nombre, const std::string& data,
		const FileDateModification& fechaMod) {
	indice << nombre;
	indice << " ";
	indice << data;
	indice << " ";
	indice << fechaMod;
	indice << '\n';
}

